<script lang="ts">
/**
 * author:hushangyue
 * create date: 2023.03.20
 */
export default {
    name: 'RelaxTable'
};
</script>
<template>
    <div>
        <el-form v-if="form" ref="formRef" :model="query" :inline="true">
            <el-form-item v-for="(item, key) in form" :key="item.id" style="margin-right: 5px">
                <el-date-picker v-if="item.type == 'daterange'" v-model="query[key]" type="daterange" unlink-panels
                    range-separator="To" start-placeholder="开始日期" end-placeholder="结束日期" :shortcuts="shortcuts"
                    :style="item.style || { width: (item.width || 260) + 'px' }" />
                <el-date-picker v-else-if="item.type == 'date' || item.type == 'datetime'" v-model="query[key]"
                    :type="item.type" class="filter-item" :placeholder="item.title || '选择日期'"
                    :style="item.style || { width: (item.width || 140) + 'px' }" />

                <el-select v-else-if="item.options" v-model="query[key]" :placeholder="item.title || '请选择'" clearable
                    :style="item.style || { width: (item.width || 120) + 'px' }" class="filter-item" filterable>
                    <el-option v-for="option in getQueryOption(item.options)" :key="option.value" :label="option.label"
                        :value="option.value" />
                </el-select>
                <el-input v-else-if="item.type == 'number'" type="number" v-model="query[key]" :placeholder="item.title"
                    clearable :style="item.style || { width: (item.width || 140) + 'px' }" class="filter-item" />
                <el-input v-else v-model="query[key]" :placeholder="item.title" clearable
                    :style="item.style || { width: (item.width || 140) + 'px' }" class="filter-item" />
            </el-form-item>

            <el-form-item>
                <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
                <el-button type="primary" style="margin-left: 40px" :icon="Document"
                    @click="handleExport">导出</el-button>
            </el-form-item>
            <el-form-item v-if="buttons && buttons.length > 0">
                <span v-for="btn in buttons" :key="btn.id">
                    <el-button :type="btn.type || 'primary'" :size="btn.size" @click="btn.click"
                        style="margin-left: 10px">
                        {{ btn.name }}
                    </el-button>
                </span>
            </el-form-item>
        </el-form>
        <el-table ref="tableRef" :highlight-current-row="!!$props.rowClick" stripe :data="list" v-loading="loading"
            :style="{ width: tableWidth }" @row-click="handleRowClick" @selection-change="handleSelectionChange" border>
            <el-table-column type="selection" width="40" align="center" />
            <el-table-column v-for="(item, key) in headers" :label="item.title || key.toString()"
                :width="columWidth(item)" :key="item.id || key" align="center" :fixed="item.fixed !== false && item.title == '操作'
                        ? 'right'
                        : (typeof item.fixed == 'object' && item.fixed) || false
                    ">
                <template #default="scope">
                    <div v-if="item.content">
                        {{
                            typeof item.content == 'function'
                                ? item.content(scope.row, key)
                                : item.content
                        }}
                    </div>
                    <div v-else-if="item.html" v-html="typeof item.html == 'function'
                            ? item.html(scope.row, key)
                            : item.html
                        "></div>
                    <div v-else-if="item.options">
                        <div v-if="item.type == 'async'">
                            {{ tempOptions[key][scope.row[key]] }}
                        </div>
                        <div v-else>
                            {{
                                typeof item.options == 'function'
                                    ? item.options(scope.row, key)
                                    : item.options[scope.row[key] || 0]
                            }}
                        </div>
                    </div>
                    <div v-else-if="item.json">
                        <div v-for="(val, k) in scope.row[key]" :key="k">
                            {{ item.json[k] || k }}:{{ val }}
                        </div>
                    </div>
                    <div v-else-if="item.type == 'time'">
                        {{
                            customParseTime(
                                scope.row[key],
                                item.format || '{y}-{m}-{d} {h}:{i}'
                            )
                        }}
                    </div>

                    <img v-else-if="item.type == 'image'" :src="scope.row[key]" alt="" :width="item.width || 60" />
                    <el-switch v-else-if="item.type == 'switch'" :active-value="!scope.row[key]" active-color="#13ce66"
                        inactive-color="#7f7f7f" :active-text="item.active" :inactive-text="item.inactive"
                        @change="item.click && item.click(scope.row, key)">
                    </el-switch>
                    <el-button v-else-if="item.type == 'button'" type="primary" size="small"
                        @click="columButtonClick(item, scope.row, key)">
                        {{
                            typeof item.label == 'function'
                                ? item.label(scope.row)
                                : item.label || (scope.row[key] ? '查看' : '')
                        }}
                    </el-button>
                    <div v-else-if="item.click" class="clickAble" @click="columButtonClick(item, scope.row, key)">
                        {{ columShowContent(item, scope.row, key) }}
                    </div>
                    <a v-else-if="item.type == 'link'" style="text-decoration: underline; color: blue"
                        :href="scope.row[key]">
                        {{ scope.row[key] || item.default }}
                    </a>
                    <div v-else-if="item.buttons">
                        <span v-for="(btn, idx) in item.buttons" :key="idx">
                            <el-button v-if="
                                operateButtonVisible(
                                    item,
                                    btn,
                                    idx,
                                    scope.row,
                                    key
                                )
                            " :type="columButtonType(btn)" size="small" @click="
                                    operateButtonClick(
                                        item,
                                        btn,
                                        idx,
                                        scope.row
                                    )
                                    " style="margin-top: 2px; margin-left: 5px">
                                {{
                                    operateButtonName(btn, idx, scope.row, key)
                                }}
                            </el-button>
                        </span>
                    </div>
                    <div v-else-if="item.table">
                        <el-table :data="scope.row[key]" style="width: 100%" type="expand">
                            <el-table-column v-for="(lb, k) in item.table" :prop="k.toString()" :label="lb" :key="k">
                            </el-table-column>
                        </el-table>
                    </div>
                    <div v-else-if="item.format">
                        {{ formatColumValue(item, scope.row, key) }}
                    </div>
                    <div v-else>
                        {{ columShowContent(item, scope.row, key) }}
                    </div>
                </template>
            </el-table-column>
        </el-table>
        <pagination v-if="total > 0" :total="total" v-model:page="query.page" v-model:limit="query.pageSize"
            @pagination="handleQuery" />
    </div>
</template>

<script lang="ts" setup>
import { Search, Document } from '@element-plus/icons-vue';

import { onMounted, reactive, ref, toRefs, onBeforeMount } from 'vue';
import { customParseTime, formatNumber } from '@/utils/index';
import { fromPairs } from 'lodash';

interface TableListProps {
    form?: {
        [key: string]: {
            title: string;
            type?: 'daterange' | 'date' | 'number' | 'datetime';
            style?: string;
            width?: number;
            options?: TOptions | Function; //选项中用
            format?: string; //日期用
            default?: any; //默认值
        };
    };
    // 搜索栏额外按钮列表
    buttons?: Array<{
        name: string;
        icon?: string;
        type?: 'primary' | 'success' | 'info' | 'warning' | 'danger';
        size?: string;
        click: Function;
    }>;

    headers: {
        [key: string]: {
            title: string;
            width?: string | number;
            type?: string;
            options?: TOptions | Function;
            content?: string | Function; //自定义内容
            click?: Function; //点击按钮
            html?: string | Function; //返回html字符串
            active?: string; //swith的时候用
            inactive?: string; //swith的时候用
            format?: string; //时间格式：{y}-{m}-{d} {h}:{i}:{s} 任意组合,非时间内置格式：查看buildInFormat函数实现
            label?: string | Function; //按钮时候用
            fixed?: string | boolean; //操作按钮是否固定在最右边
            json?: any; //用户显示json数据
            default?: any;
            table?: any; //内置小的表格

            buttons?: Array<{ name: string; click: Function }>; //操作按钮列表
        };
    };

    // 网络请求函数
    fetch: (query: any) => { list: any[]; total: number };

    rowClick?: (row: any) => void;
    selectionChange?: (row: any) => void;
}

const props = withDefaults(defineProps<TableListProps>(), {
    form: undefined,
    headers: undefined
});

const state = reactive({
    loading: false,
    list: [
        { a: 1, b: 'bb', c: 3 },
        { a: 2, b: 'bb2', c: 'c' }
    ] as any[],
    query: {
        page: 1,
        pageSize: 20
    } as any,
    tableWidth: '100%',
    total: 0,
    tempOptions: {} as { [key: string]: { [opionValue: string]: string } }
});
const {
    loading,
    list,
    total,
    query,
    tempOptions: tempOptions,
    tableWidth
} = toRefs(state);
// const dateRange = ref('')

// 快捷按钮
const shortcuts = [
    {
        text: 'Last week',
        value: () => {
            const end = new Date();
            const start = new Date();
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
            return [start, end];
        }
    },
    {
        text: 'Last month',
        value: () => {
            const end = new Date();
            const start = new Date();
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
            return [start, end];
        }
    },
    {
        text: 'Last 3 months',
        value: () => {
            const end = new Date();
            const start = new Date();
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
            return [start, end];
        }
    }
];

function getQueryOption(
    options: Function | TOptions
): Array<{ value: string | number; label: string }> {
    let opts: any = [];
    let temp: any = typeof options == 'function' ? options() : options;
    for (let k in temp) {
        let opt: any = temp[k];

        if (typeof opt == 'string' || typeof opt == 'number') {
            opts.push({ value: k, label: opt });
        } else {
            opts.push({ value: opt.value, label: opt.label });
        }
    }
    return opts;
}

function getQueryData() {
    let query: any = {
        page: state.query.page,
        pageSize: state.query.pageSize
    };

    if (props.form) {
        for (let k in props.form) {
            let item = props.form[k];

            if (item.type == 'daterange') {
                if (state.query[k]) {
                    query[k] = [
                        (state.query[k][0] as any).getTime(),
                        (state.query[k][1] as any).getTime()
                    ];
                }
            } else if (item.type == 'date' || item.type == 'datetime') {
                if (state.query[k]) {
                    query[k] = (state.query[k] as any).getTime();
                }
            } else {
                if (!state.query[k]) {
                    continue;
                }
                if (item.type == 'number') {
                    query[k] = parseInt(state.query[k]);
                } else {
                    query[k] = state.query[k];
                }
            }
        }
    }

    console.log('get query', query);
    return query;
}
/**
 * 查询
 */
async function handleQuery() {
    state.loading = true;

    let data = await props.fetch(getQueryData());

    state.list = data.list;
    state.total = data.total;
    state.loading = false;
}
// 导出为execel
function handleExport() {
    console.log('该功能暂不支持');
}

// 内置格式化函数，常用，金币，百分比等
function buildInFormat(row: any, key: string | number, format: string) {
    if (row[key] && format) {
        switch (format) {
            case '%':
                return row[key].toString().toFixed(2) + '%'; //默认百分制
            case '‰':
                return row[key].toString().toFixed(2) + '‰'; //默认百分制
            case '100%':
                return (parseFloat(row[key].toString()) * 100).toFixed(2) + '%'; //小数点，方式需要乘以100
            case '1000‰':
                return (
                    (parseFloat(row[key].toString()) * 1000).toFixed(2) + '‰'
                );
            case '/100%':
                return (parseFloat(row[key].toString()) / 100).toFixed(2) + '%';
            case '/1000‰':
                return (
                    (parseFloat(row[key].toString()) / 1000).toFixed(2) + '‰'
                );
            case '￥':
            case 'RMB':
                return '￥' + (parseInt(row[key].toString()) / 100).toFixed(2);
            case '$':
            case 'US':
                return '$' + (parseFloat(row[key].toString()) / 100).toFixed(2);
            case 'w':
            case 'W':
            case 'coin':
                return formatNumber(
                    parseInt(row[key].toString()),
                    "wan"
                );
            case ',':
                return formatNumber(
                    parseInt(row[key].toString()),
                    "thousand"
                );
            case 'k':
            case 'K':
                return formatNumber(
                    parseInt(row[key].toString()),
                    "million"
                );
            case '+':
            case 'winlose':
                return formatNumber(
                    parseInt(row[key].toString()),
                    "winlose"
                );
            case '…': //省略显示长字符串
                let a = row[key].toString();
                return a.length > 15 ? a.slice(0, 15) + '…' : a;
        }

        // 暂未实现
        return row[key];
    }
    return '';
}
// 一些常用的格式化
function formatColumValue(item: any, row: any, key: string | number) {
    if (typeof item.format == 'function') {
        return item.format(row, key);
    }
    console.log(
        'fm==== ',
        row,
        key,
        row[key],
        buildInFormat(row, key, item.format)
    );
    return buildInFormat(row, key, item.format) || item.default;
}

//普通项按钮事件
function columButtonClick(item: any, row: any, key: string | number) {
    if (item.click) {
        item.click(row);
    } else {
        console.log('按钮事件未定义');
    }
}
function columButtonType(button: any) {
    if (button.type) return button.type;
    const operate = button.name || button;
    return operate == 'delete' || operate == '删除' ? 'danger' : 'primary';
}

function operateButtonVisible(
    head: any,
    button: any,
    idx: number,
    row: any,
    key: string | number
) {
    const visbleCheck = button.visible || head.visible;
    if (typeof visbleCheck == 'function') {
        if (visbleCheck(row, key)) {
            return operateButtonName(button, idx, row, key);
        }
        return false;
    }
    return operateButtonName(button, idx, row, key);
}
function operateButtonName(
    button: any,
    idx: number,
    row: any,
    key: string | number
) {
    if (button.name) {
        if (typeof button.name == 'function') {
            return button.name(row, idx);
        }
        return button.name;
    } else if (typeof button == 'string') {
        return button;
    }
    return '操作';
}
function operateButtonClick(head: any, button: any, idx: number, row: any) {
    let click = head.click || button.click;
    if (click) {
        click(row, idx);
    } else {
        console.log('操作按钮处理函数未定义');
    }
}

function columShowContent(head: any, row: any, key: string | number) {
    if (typeof head.content == 'function') {
        //显示内容用户自定义，传入参数整行
        return head.content(row);
    } else if (head.options) {
        return head.options[row[key]] || '';
    }
    let val = row[key] || head.default;

    return val === undefined ? 0 : val;
}

function columWidth(item: any) {
    if (item.width) return item.width;
    if (item.type == 'time') return 160;
    if (item.type == 'date') return 100;
    if (item.type == 'image') return 80;

    const name = item.title || item;
    if (item.type == 'button') {
        return 42 + name.length * 15;
    }

    if (item.type == 'operate' || item.buttons) {
        return 25 + item.buttons.length * 68;
    }
    return 36 + (name.length - 1) * 18;
}

async function fetchAsyncOption(asyncFunc: Function, key: string) {
    let result = await asyncFunc();
    tempOptions.value[key] = {};
    for (let k in result) {
        if (result[k] && result[k].label) {
            //{value,label}形式
            tempOptions.value[key][result[k].value || '0'] = result[k].label;
        } else {
            tempOptions.value[key][k] = result[k];
        }
    }
}

// 行勾选器发生变化响应
function handleSelectionChange(selection: any) {
    // state.ids = selection.map((item: any) => item.id);
    if (props.selectionChange) {
        props.selectionChange(selection);
    }
}

// 行选中或者取消选中响应函数
function handleRowClick(row: any) {
    if (props.rowClick) {
        props.rowClick(row);
    }
}

function refresh() {
    handleQuery();
}
defineExpose({ refresh });

onMounted(() => {
    if (props.form) {
        for (let k in props.form) {
            let item = props.form[k];
            if (item.default !== undefined) {
                // state.query[k].value = item.default;

                state.query[k] = item.default;
            }
        }
    }

    let width = 0;
    for (let k in props.headers) {
        let item = props.headers[k];
        width += columWidth(item);

        // 需要动态去获取的options，必须手动指定type为async
        if (typeof item.options == 'function' && item.type == 'async') {
            fetchAsyncOption(item.options, k).then();
        }
    }
    tableWidth.value = width + 40 + 'px';

    handleQuery().then();
});
</script>

<style scoped>
.clickAble {
    color: blue;
    cursor: pointer;
}
</style>
